/***********************************************************************************
 * 文件名： bmlite.c
 * 版本： 9
 * 说明：
 * 		一个极度精简的电池管理组件，提供基本的电池事件通知。
 *      在合适的地方调用 BMLite_Loop() 确保其在100ms左右执行一次。在程序启动时调用一次
 *  BMLite_Init() 初始化本组件。通过 BMLite_SetRefreshStatus 可以暂时关闭或者启动状态刷新。
 *  可以调用对应的函数接口主动获取一些状态，这些接口在头文件中有申明和说明。当有状态变化时，
 *  本组件会通过调用 BMLite_Event 来进行通知，你必须在需要接收通知的地方定义 BMLite_Event 
 *  函数，它的使用类似一个中断函数，通过名称来识别，函数原型为 
 *  void BMLite_Event(bmlite_ev_t e)。
 *      必须在 bmlite_config.h 文件中进行合适的配置，本组件才能工作在你期望的状态。
 * 修改记录：
 * 	2021/04/29: 初版。 刘言。
 *  2021/05/07: 增加获取电池电量的功能。刘言。
 *  2021/05/17: 增加当启用SWST且充电器接入时，仅在MOS开启时进行ADC采集的功能。刘言。
 *  2021/07/21: 当启用SWST且充电器接入时可选ADC采集时机：任意、MOS导通、MOS关断。
 *  2021/07/22: 当启用SWST时仅在MOS关断（停止充电）时检测充电器是否接入。
 *  2021/08/16: 当充电器接入时，先调用通知充电器接入再进行其它处理；某些属性增加NO_INIT修饰。
 *  2021/08/17: 添加"满电之后继续开启MOS，直到过充保护电压"配置项
 *  2021/09/06: 电量获取更准确，增加配置更新电量的间隔时间。
 *  2022/04/08: 解决边充边用的两个BUG
***********************************************************************************/
#include "bmlite.h"
#include "..\..\polsys.h"

// 预处理

#if (_BMLITE_MAX_ADV > 256)
typedef u16 bmlite_adv_t;
#else
typedef u8 bmlite_adv_t;
#endif

#if (_BMLITE_AD_MODE == 0)
#define _BMLITE_FLOAT_VOLTAGE_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_FLOAT_VOLTAGE)
#define _BMLITE_OVER_VOLTAGE_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_OVER_VOLTAGE)
#define _BMLITE_NO_BATTERY_VOLTAGE_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_NO_BATTERY_VOLTAGE)
#define _BMLITE_HAS_BATTERY_VOLTAGE_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_HAS_BATTERY_VOLTAGE)
#else
#define _BMLITE_FLOAT_VOLTAGE_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_FLOAT_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_OVER_VOLTAGE_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_OVER_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_NO_BATTERY_VOLTAGE_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_NO_BATTERY_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_HAS_BATTERY_VOLTAGE_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_HAS_BATTERY_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#endif

#define _BMLITE_FLOAT_TIME_VAL (_BMLITE_FLOAT_TIME * 10)

// 属性定义

bit _BMLite_mIsCharging = false;
bit _BMLite_mDCConnected = false;
NO_INIT bit _BMLite_mRunning;
bit _BMLite_mNoBattery = false;
NO_INIT static bit BMLite_mFull;
NO_INIT static u16 BMLite_mFloatTime;

#ifdef _BMLITE_USE_SWST
static u8 BMLite_mDcInTime;
#endif

#ifdef _BMLITE_USE_DC_OUT_DELAY
NO_INIT static u8 BMLite_mDcOutTime;
#endif

#ifdef _BMLITE_USE_BATT_LEVEL
u8 _BMLite_mLevel; // 电池电量百分比
u16 _BMLite_mChangeLevelTime = 0;   // 改变电量计时

static void UpdateLevel(bmlite_adv_t adv);
static u8 AdvToVolume(bmlite_adv_t adv);

#if (_BMLITE_AD_MODE == 0)
#define _BMLITE_L100_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_L100_VOLTAGE)
#define _BMLITE_L90_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_L90_VOLTAGE)
#define _BMLITE_L80_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_L80_VOLTAGE)
#define _BMLITE_L70_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_L70_VOLTAGE)
#define _BMLITE_L60_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_L60_VOLTAGE)
#define _BMLITE_L50_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_L50_VOLTAGE)
#define _BMLITE_L40_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_L40_VOLTAGE)
#define _BMLITE_L30_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_L30_VOLTAGE)
#define _BMLITE_L20_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_L20_VOLTAGE)
#define _BMLITE_L10_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_L10_VOLTAGE)
#define _BMLITE_L5_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_L5_VOLTAGE)
#define _BMLITE_L0_ADV (bmlite_adv_t)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / _BMLITE_L0_VOLTAGE)
#else
#define _BMLITE_L100_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_L100_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_L90_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_L90_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_L80_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_L80_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_L70_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_L70_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_L60_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_L60_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_L50_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_L50_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_L40_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_L40_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_L30_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_L30_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_L20_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_L20_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_L10_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_L10_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_L5_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_L5_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#define _BMLITE_L0_ADV (bmlite_adv_t)((u32)((u32)_BMLITE_L0_VOLTAGE * _BMLITE_LOW_RES / (_BMLITE_HIGH_RES + _BMLITE_LOW_RES)) * _BMLITE_MAX_ADV / _BMLITE_ADC_VREF)
#endif

static C51_CODE const bmlite_adv_t mVolToDlTable[] = {
    _BMLITE_L0_ADV, 
    _BMLITE_L5_ADV, 
    _BMLITE_L10_ADV, 
    _BMLITE_L20_ADV, 
    _BMLITE_L30_ADV, 
    _BMLITE_L40_ADV, 
    _BMLITE_L50_ADV, 
    _BMLITE_L60_ADV, 
    _BMLITE_L70_ADV, 
    _BMLITE_L80_ADV, 
    _BMLITE_L90_ADV, 
    _BMLITE_L100_ADV
    };
#endif


// 声明引用事件通知函数（避免使用函数指针的妥协）
// 在你的应用代码中定义这个函数用于接收事件
void BMLite_Event(bmlite_ev_t e);


// 对外提供的函数

void BMLite_Init()
{
    _BMLite_mRunning = true;
#ifdef _BMLITE_USE_BATT_LEVEL
    bmlite_adv_t adv;
    adv = _BMLite_GetADValue();
    _BMLite_mLevel = AdvToVolume(adv);
#endif
}

#ifdef _BMLITE_USE_GET_VOLTAGE
u16 BMLite_GetBattVoltage()
{
    bmlite_adv_t adv;
    u16 vbat;
    adv = _BMLite_GetADValue();
#if (_BMLITE_AD_MODE == 0)
    vbat = (u16)((u32)_BMLITE_VREF * _BMLITE_MAX_ADV / adv);
#else
    vbat = (u16)((u32)_BMLITE_ADC_VREF * (_BMLITE_HIGH_RES + _BMLITE_LOW_RES) * adv / _BMLITE_LOW_RES  / _BMLITE_MAX_ADV);
#endif
    return vbat;
}
#endif

void BMLite_Loop() //100ms执行一遍
{
    if (_BMLite_mRunning)
    {
        // 获取电池电压AD值
#ifdef _BMLITE_USE_SWST                 // 启用了软件控制的充电启停
    #if (_BMLITE_ADC_TIMING == 0)       // 随时都可以获取电池AD值
        bmlite_adv_t adv;
        adv = _BMLite_GetADValue();
    #else                               // 特定情况下才能获取电池AD值
        static bmlite_adv_t adv;
        if (_BMLite_mDCConnected)
        {
        #if (_BMLITE_ADC_TIMING == 1)       // 充电器接入时，仅在MOS导通时采集电池AD值
            if (BMLite_mDcInTime >= 1)
        #elif (_BMLITE_ADC_TIMING == 2)     // 充电器接入时，仅在MOS断开时采集电池AD值
            if (BMLite_mDcInTime < 1)
        #endif
                adv = _BMLite_GetADValue();
        }
        else                            // 充电器未接入时随时可以获取电池AD值
            adv = _BMLite_GetADValue();
    #endif
#else                                   // 未启用软件控制的充电启停功能时随时可以获取电池AD值
        bmlite_adv_t adv;
        adv = _BMLite_GetADValue();
#endif
        // 更新电池电量
#ifdef _BMLITE_USE_BATT_LEVEL
        UpdateLevel(adv);
#endif
#ifdef _BMLITE_USE_SWST
        if(BMLite_mDcInTime > 0 || _BMLite_GetDCStatus())
#else
        if (_BMLite_GetDCStatus()) //充电器接入
#endif
        {
            if (!_BMLite_mDCConnected) // 之前未接入
            {
                _BMLite_mDCConnected = true;
                BMLite_Event(BMLITE_EV_DC_PULL_IN); // 通知充电器接入事件
            }
#ifdef _BMLITE_USE_DC_OUT_DELAY
            BMLite_mDcOutTime = 0;
#endif
#ifdef _BMLITE_USE_SWST
            BMLite_mDcInTime++;
            if (BMLite_mDcInTime == 10)
            {
                BMLite_mDcInTime = 0;
                _BMLite_StopChrg();
            }
            else if (BMLite_mDcInTime == 1)
            {
#ifdef _BMLITE_USE_MOS_CONTINUE_OPEN
    #if (_BMLITE_AD_MODE == 0)
                if (adv > _BMLITE_OVER_VOLTAGE_ADV)
    #else
                if (adv < _BMLITE_OVER_VOLTAGE_ADV)
    #endif
#else
                if (BMLite_mFull == false)
#endif
                {
                    _BMLite_StartChrg();
                }
            }
#endif
#if (_BMLITE_AD_MODE == 0)
            if (adv > _BMLITE_FLOAT_VOLTAGE_ADV)
#else
            if (adv < _BMLITE_FLOAT_VOLTAGE_ADV)
#endif
            {
                BMLite_mFull = false;
                // hjm 2022年4月8日：清除满电标记的同时，需要清除累计时间，否则边充边用可能会出现多次提示满电的现象（LED闪烁次数可能不对）
                BMLite_mFloatTime = 0;
            }
#if (_BMLITE_AD_MODE == 0)
            else if (adv > _BMLITE_OVER_VOLTAGE_ADV)
#else
            else if (adv < _BMLITE_OVER_VOLTAGE_ADV)
#endif
            {
                BMLite_mFloatTime++;
                if (BMLite_mFloatTime >= _BMLITE_FLOAT_TIME_VAL)
                {
                    BMLite_mFull = true;
                }
            }
            else
            {
                BMLite_mFull = true;
            }
            if (BMLite_mFull == false) // 不是满电，说明正在充电
            {
                if (_BMLite_mIsCharging == false)
                {
                    _BMLite_mIsCharging = true;
                    BMLite_Event(BMLITE_EV_START_CHARGE); // 通知开始充电
                }
            }
            else // 充满了
            {
                if (_BMLite_mIsCharging)
                {
                    _BMLite_mIsCharging = false;
                    BMLite_Event(BMLITE_EV_CHARGE_FULL); // 通知已充满（停止充电）
                }
            }
            if (_BMLite_mNoBattery)
            {
#if (_BMLITE_AD_MODE == 0)
                if (adv < _BMLITE_HAS_BATTERY_VOLTAGE_ADV)
#else
                if (adv > _BMLITE_HAS_BATTERY_VOLTAGE_ADV)
#endif
                {
                    _BMLite_mNoBattery = false;
                    //BMLite_Event(BMLITE_EV_HAS_BATTERY);   // 通知有电了
                }
            }
            // hjm 2022年4月8日：尾部充电的一般都支持边充边用，这个时候电压低不标记没电，实测可能导致没电提示为短路（猜测电压过低ADC异常）
            else
            {
#if (_BMLITE_AD_MODE == 0)
                if (adv > _BMLITE_NO_BATTERY_VOLTAGE_ADV)
#else
                if (adv < _BMLITE_NO_BATTERY_VOLTAGE_ADV)
#endif
                {
                    _BMLite_mNoBattery = true;
                    //BMLite_Event(BMLITE_EV_NO_BATTERY);   // 通知没电了
                }
            }
        }
        else // 充电器移除了
        {
#ifdef _BMLITE_USE_DC_OUT_DELAY
            if (BMLite_mDcOutTime < 255)
                BMLite_mDcOutTime++;
            if (BMLite_mDcOutTime >= _BMLITE_DC_OUT_DELAY_TIME / 100)
#endif
            {
#if (_BMLITE_AD_MODE == 0)
                if (adv > _BMLITE_FLOAT_VOLTAGE_ADV)
#else
                if (adv < _BMLITE_FLOAT_VOLTAGE_ADV)
#endif
                {
                    BMLite_mFull = false;
                    BMLite_mFloatTime = 0;
                }
                if (!_BMLite_mNoBattery)
                {
#if (_BMLITE_AD_MODE == 0)
                    if (adv > _BMLITE_NO_BATTERY_VOLTAGE_ADV)
#else
                    if (adv < _BMLITE_NO_BATTERY_VOLTAGE_ADV)
#endif
                    {
                        _BMLite_mNoBattery = true;
                        //BMLite_Event(BMLITE_EV_NO_BATTERY);   // 通知没电了
                    }
                }
                if (_BMLite_mDCConnected)
                {
                    _BMLite_mDCConnected = false;
                    _BMLite_mIsCharging = false;
#ifdef _BMLITE_USE_SWST
                    BMLite_mDcInTime = 0;
#endif
                    BMLite_Event(BMLITE_EV_DC_PULL_OUT);
                }
            }
        }
        // TempProtect();      // 温度保护
    }
}

#ifdef _BMLITE_USE_BATT_LEVEL
// 更新电量
static void UpdateLevel(bmlite_adv_t adv)
{
    u16 update_inte;     // 更新电量的间隔时间

    if(BMLite_mFull)
    {
        _BMLite_mLevel = 100;
        return;
    }
    if (_BMLite_mIsCharging)
        update_inte = _BMLITE_LEVEL_INTV_C*10; //充电时间隔 _BMLITE_LEVEL_INTV_C 秒更新电量
    else
        update_inte = _BMLITE_LEVEL_INTV_D*10; //放电时间隔 _BMLITE_LEVEL_INTV_D 秒更新电量
    if (_BMLite_mChangeLevelTime >= update_inte)		 //间隔至少 update_inte 秒才能改变电池电量
    {
        u8 volume;
        _BMLite_mChangeLevelTime = 0;
        volume = AdvToVolume(adv);
        if (volume < 100 && volume > _BMLite_mLevel) //电量增加了
        {
            _BMLite_mLevel++;
        }
        else if (volume < _BMLite_mLevel) //电量降低了
        {
            if (volume <= 5)
                _BMLite_mLevel = volume;
            else if (_BMLite_mLevel - volume > 5)
                _BMLite_mLevel = volume;
            else
                _BMLite_mLevel--;
        }
    }
    else
        _BMLite_mChangeLevelTime++;
}

//单纯通过AD值粗略计算电量
static u8 AdvToVolume(bmlite_adv_t adv)
{
	u8 level, i;

#if (_BMLITE_AD_MODE == 0)
    for (i = 0; i < 12; i++)
	{
		if (adv > mVolToDlTable[i])
			break;
	}
	i--;
	if (i == 255) //电压小于电量0的电压
	{
		level = 0;
	}
	else if (i >= 2)
	{
		level = (i - 1) * 10;
		level += (mVolToDlTable[i] - adv) * 10 / (mVolToDlTable[i] - mVolToDlTable[i + 1]);
	}
	else //0,1
	{
		level = i * 5;
		level += (mVolToDlTable[i] - adv) * 5 / (mVolToDlTable[i] - mVolToDlTable[i + 1]);
	}
#else
	for (i = 0; i < 12; i++)
	{
		if (adv < mVolToDlTable[i])
			break;
	}
	i--;
	if (i == 255) //电压小于电量0的电压
	{
		level = 0;
	}
	else if (i >= 2)
	{
		level = (i - 1) * 10;
		level += (adv - mVolToDlTable[i]) * 10 / (mVolToDlTable[i + 1] - mVolToDlTable[i]);
	}
	else //0,1
	{
		level = i * 5;
		level += (adv - mVolToDlTable[i]) * 5 / (mVolToDlTable[i + 1] - mVolToDlTable[i]);
	}
#endif
	return level;
}
#endif
